
/************************ item_30: 透彻了解 inlining 的里里外外 *****************************/

// 记住，inline只是对编译器的一个申请，不是强制命令。这项申请可以隐喻提出，也可以明确提出。隐喻方式是将函数定义于 class 定义式内：
class Person {
    public:
        // ...
        int age() const {return theAge;}    //一个隐喻的 inline 申请：age 被定义于 class 定义式内
        // ...
    private:
        int theAge;
};

// 这样的函数通常是成员函数，但条款46说friend函数也可能被定义于class内，如果真是那样，他们也是被隐喻声明为inline。

// 明确声明 inline 函数的做法则是在其定义式前加上关键字 inline。例如标准的 max template（来自<algorithm>）往往这样实现出来：
template<typename T>                                //明确申请inline
inline const T& std::max(const T& a, const T& b) {  //std::max之前有关键字 "inline"
    return a < b ? b : a;
}

// 一个表面上看似 inline 的函数是否真是 inline，取决于你的建置环境，主要取决于编译器。幸运的是大多数编译器提供了一个诊断级别：如果它们无法将你要求
// 的函数 inline 化，会给你一个警告信息。

// 编译器通常不对 "通过函数指针而进行的调用" 实施 inlining，这意味着对 inline 函数的调用有可能被 inlined，也可能不被 inlined，取决于该调用的实施方式：
inline void f() {           //假设编译器有意愿 inline "对f的调用"
    // ...
}

void (*pf)() = f;           //pf 指向 f

// ...
f();                        //这个调用将被 inlined，因为它是一个正常调用。
pf();                       //这个调用或许不被 inlined，因为它通过函数指针达成。

// 构造函数和析构函数往往是 inlining 的糟糕候选人 --- 考虑以下 Derived class 构造函数：
#include <string>

class Base {
    public:
    // ...
    private:
        std::string bm1, bm2;       // base 成员1和2
};

class Derived: public Base {
    public:
        Derived() {}                // Derived 构造函数是空的，对吗？
        // ...
    private:
        std::string dm1, dm2, dm3;  // derived 成员1-3
};

// 这个构造函数看起来的 inlining 的绝佳候选人，因为他根本不含任何代码。但是你的眼睛可能会欺骗你
// 编译器为稍早说的那个表面上看起来为空的 Derived 构造函数所产生的代码，相当于以下所列：
Derived::Derived() {                        // "空白 Derived构造函数"的观念性实现
    Base::Base();                           // 初始化 "Base成分"
    try {
        dm1.std::string::string();          // 试图构造 dm1
    } catch (...) {                         // 如果抛出异常就销毁 base class 成分，并传播该异常
        Base::~Base();
        throw;
    }

    try {
        dm2.std::string::string();          // 试图构造 dm2
    } catch (...) {                         // 如果抛出异常就
        dm1.std::string::~string();         // 销毁dm1
        Base::~Base();                      // 销毁 base class 成分，
        throw;                              // 并传播该异常
    }

    try {
        dm3.std::string::string();          // 试图构造 dm3
    } catch (...) {                         // 如果抛出异常就
        dm2.std::string::~string();         // 销毁dm2
        dm1.std::string::~string();         // 销毁dm1
        Base::~Base();                      // 销毁 base class 成分，
        throw;                              // 并传播该异常
    }
}

// 总结：
// 1. 将大多数 inlining 限制在小型、被频繁调用的函数身上。这可使日后的调试过程和二进制升级更容易，也可使潜在的代码膨胀问题最小化，是程序的速度提升机
// 会最大化
// 2. 不要只因为 function templates 出现在头文件，就将它们声明成 inline。
